<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <!-- <link rel="stylesheet" href="./uikit/css/uikit.min.css"> -->
    <style>
        .box {
            width: 100%;
            height: 100vh;
            background-color: #d1c9c9;
            display: flex;
            justify-content: center;
            align-items: center;
            flex-direction: row;
        }

        .center {
            width: 70%;
            height: 100%;
            background-color: #dbdbdb;
            /* display: flex;
            justify-content: center;
            align-items: center; */
            display: block;
            overflow: scroll;
        }

        .option {
            width: 30%;
            height: 100%;
            overflow: scroll;
            background-color: #f8f2e5;
            display: block;
            /* justify-content: center;
            align-items: center;
            flex-direction: column; */
        }

        .option-item {
            display: flex;
            justify-content: flex-start;
            margin: 5px;
            width: 90%;
        }

        #canvasP canvas {
            border: 1px solid #fefefe !important;
            margin: 10px;
        }

        .input1 {
            width: 20%;
        }

        .value {
            display: block;
            width: 100%;
            padding: 10px;
            box-sizing: border-box;
        }

        .value-item {
            display: inline-block;
            margin: 10px;
            padding: 15px;
            border: 1px solid #b6b1b1;

        }
    </style>
</head>

<body>
    <div class="box">
        <div class="center" id="canvasP">
            <canvas id="myCanvas" width="300" height="300"></canvas>
            <div id="myCanvasList"></div>
        </div>
        <div class="option">
            <form action="javascript:" class="form1">
                <div class="option-item">
                    <div style="margin: 5px;">画布宽度</div>
                    <input type="number" name="CanvasWeight" value="300">
                </div>
                <div class="option-item">
                    <div style="margin: 5px;">光源位置</div>
                    <input type="number" style="display: none;" name="SourceX" value="50" placeholder="光源位置的x轴">
                    <input type="number" name="SourceY" value="300" min="270" placeholder="光源位置的y轴">
                </div>
                <div class="option-item">
                    <div style="margin: 5px;">遮罩圆心</div>
                    <input type="number" name="MaskX" value="60" placeholder="遮罩圆心的x轴">
                    <input type="number" name="MaskY" value="" placeholder="和底部相切，可以不填y轴">
                    <input type="number" name="MaskR" value="30" placeholder="半径">
                </div>
                <div class="option-item">
                    <div style="margin: 5px;">开口角度（角度数）</div>
                    <input type="number" name="Opening1" value="50" placeholder="遮罩开口开始">
                    <input type="number" name="Opening2" value="110" placeholder="遮罩开口结束">
                </div>
                <div class="option-item">
                    <div style="margin: 5px;">曲线下部分控制点</div>
                    <!-- <input type="number" name="curveC_width" value="25" placeholder="曲线宽度"> -->
                    <input type="number" name="curveC_x" value="80" placeholder="曲线控制点x">
                    <input type="number" name="curveC_y" value="240" placeholder="曲线控制点y">
                </div>
                <div class="option-item">
                    <div style="margin: 5px;">曲线上部分控制点</div>
                    <div class="">
                        <input type="number" style="height: 40px;display: none;" name="direction_start_x" value="37"
                            placeholder="曲线起点x">
                        <input type="number" style="height: 40px;display: none;" name="direction_start_y" value="186"
                            placeholder="曲线起点y">
                        <input type="number" style="height: 40px;" name="direction_C_x" value="38" placeholder="曲线控制点x">
                        <input type="number" style="height: 40px;" name="direction_C_y" value="60"
                            placeholder="曲线控制点y">
                        <input type="number" style="height: 40px;display: none;" name="direction_end_x" value="149"
                            placeholder="曲线终点x">
                        <input type="number" style="height: 40px;display: none;" name="direction_end_y" value="186"
                            placeholder="曲线终点y">
                    </div>
                </div>
                <div class="option-item">
                    <div style="margin: 5px;">曲线宽度/高度</div>
                    <input type="number" name="curveC_width" value="10" placeholder="曲线宽度">
                    <input type="number" name="curveC_height" value="155" placeholder="曲线宽度">
                </div>
                <div class="option-item">
                    <button type="submit">绘制图形</button>
                    <button type="reset">重置</button>
                    <button type="" onclick="complex()" style="color: red;">初步展示部分条件</button>
                    <button type="" onclick="downLoad()" style="color: red;">下载图片png</button>
                </div>
            </form>
            <form class="form_2" action="javascript:">
                <div class="option-item " style="flex-direction: column;">
                    <div style="margin: 5px;">选择展示条件:</div>
                    <div>
                        <input id="na" name="Source" class="uk-checkbox" type="checkbox">
                        <label class="uk-form-label" for="na">光源位置</label>
                    </div>
                    <div>
                        <input id="na2" name="na12" class="uk-checkbox" type="checkbox">
                        <label for="na2" class="uk-form-label">遮罩圆心位置</label>
                    </div>
                    <div>
                        <input id="na3" name="na2" class="uk-checkbox" type="checkbox">
                        <label for="na3" class="uk-form-label">遮罩圆心半径</label>
                    </div>
                    <div>
                        <input id="na4" name="na2" class="uk-checkbox" type="checkbox">
                        <label for="na4" class="uk-form-label">开口角度</label>
                    </div>
                    <div>
                        <input id="na5" name="na2" class="uk-checkbox" type="checkbox">
                        <label for="na5" class="uk-form-label">曲线宽度</label>
                    </div>
                    <div>
                        <input id="na6" name="na2" class="uk-checkbox" type="checkbox">
                        <label for="na6" class="uk-form-label">曲线控制点</label>
                    </div>
                </div>
                <div class="option-item">
                    <button type="" onclick=" " style="color: red;">展示</button>
                </div>
            </form>
            <div class="value">
                <!-- <div class="value-item " onclick="setFormValue()"></div> -->
            </div>
        </div>
    </div>

    <script src="./jquery.3.5.1.js"></script>
    <!-- <script src="./uikit/js/uikit.min.js"></script> -->

    <script>
        class Local {
            constructor() {
                this.initLo();
            }
            setData(key, value) { // 保存键值对到本地
                if (typeof value === 'string') {
                    return localStorage.setItem(key, value);
                }
                try {
                    localStorage.setItem(key, JSON.stringify(value));
                } catch (err) {
                    return err;
                }
            }
            getData(key) { // 获取指定key的数据
                const value = localStorage.getItem(key);
                try {
                    return JSON.parse(value);
                } catch (err) {
                    return value;
                }
            }
            removeData(key) { // 删除指定key的数据
                return localStorage.removeItem(key);
            }
            clearData() { // 删除所有的本地数据
                return localStorage.clear();
            }
            initLo() { // 初始化
                if (!window.localStorage) {
                    throw new Error("浏览器不支持 localstorage 本地存储！！！")
                }
            }
        }
        var local = new Local()

        function setData(data) {
            return local.setData('test', data)
        }

        function getData(data) {
            return local.getData('test')
        }
    </script>
    <script>
        // setInterval(setPoint, 100)

        var formData = {},
            myCanvas = ''

        // 获取到表单的值
        function getFormData() {
            var form = $('.form1').serializeArray()
            for (let index = 0; index < form.length; index++) {
                const element = form[index];
                if (!element.name) continue;
                formData[element.name] = Number(element.value);
            }
            return form
        }

        $("form.form1").submit(function (e) {
            // console.log('', $('form').serializeArray())
            var form = getFormData()
            setData(form)
            // console.log('', formData)
            // location.reload()
            verification(formData)
        });

        ;
        (function init() {
            var form__ = getData() || []
            form__.forEach(function (item) {
                $(`[name="${item.name}"]`).val(item.value)
            })
            for (let index = 0; index < form__.length; index++) {
                const element = form__[index];
                if (!element.name) continue;
                formData[element.name] = element.value;
                if (element.name == 'CanvasWeight')
                    $('#myCanvas').attr({
                        width: element.value,
                        height: element.value,
                    })
            }
            verification(formData)
        })()

        // 监听表单
        $('.form1 [name="CanvasWeight"]').change(function (res) {
            var $this = $(this);
            console.log('', $this.val())
            $('#myCanvas').attr({
                width: $this.val(),
                height: $this.val(),
            })
            $('[name="SourceY"]').attr({
                min: $this.val() - $('[name="MaskR"]').val(),
            })
            // if ($('[name="SourceY"]').val() < $this.val()) $('[name="SourceY"]').val($this.val() - $(
            //     '[name="MaskR"]').val())
            $('[name="SourceY"]').val($this.val() * formData.SourceY / formData.CanvasWeight)
            $('[name="MaskX"]').val($this.val() * formData.MaskX / formData.CanvasWeight)
            $('[name="MaskY"]').val($this.val() * formData.MaskY / formData.CanvasWeight)
            $('[name="MaskR"]').val($this.val() * formData.MaskR / formData.CanvasWeight)
            $('[name="curveC_width"]').val($this.val() * formData.curveC_width / formData
                .CanvasWeight)
            $('[name="curveC_x"]').val($this.val() * formData.curveC_x / formData.CanvasWeight)
            $('[name="curveC_y"]').val($this.val() * formData.curveC_y / formData.CanvasWeight)

            $('[name="direction_start_x"]').val($this.val() * formData.direction_start_x / formData
                .CanvasWeight)
            $('[name="direction_start_y"]').val($this.val() * formData.direction_start_y / formData
                .CanvasWeight)
            $('[name="direction_C_x"]').val($this.val() * formData.direction_C_x / formData
                .CanvasWeight)
            $('[name="direction_C_y"]').val($this.val() * formData.direction_C_y / formData
                .CanvasWeight)
            $('[name="direction_end_x"]').val($this.val() * formData.direction_end_x / formData
                .CanvasWeight)
            $('[name="direction_end_y"]').val($this.val() * formData.direction_end_y / formData
                .CanvasWeight)
            $('[name="curveC_height"]').val($this.val() * formData.curveC_height / formData
                .CanvasWeight)

            verification(formData)
        })
        $('.form1 input').change(function () {
            var form = getFormData()
            verification(formData)
        })

        function downLoad() {
            var url = myCanvas.toDataURL("image/png");
            var oA = document.createElement("a");
            oA.download = '图片'; // 设置下载的文件名，默认是'下载'
            oA.href = url;
            document.body.appendChild(oA);
            oA.click();
            oA.remove(); // 下载之后把创建的元素删除
        }

        function complex(formData_ = formData) {
            var form = $('.form1').serializeArray()
            for (let index = 0; index < form.length; index++) {
                const element = form[index];
                if (!element.name) continue;
                formData[element.name] = element.value;
            }

            SourceY = complexData(formData.SourceY, 30, 3) // 光源位置

            MaskX = complexData(formData_.MaskX, 30, 3) // 遮罩圆心x轴
            // MaskR = complexData(formData_.MaskR, 30, 3) // 遮罩圆心半径

            // Opening1 = complexData(formData_.Opening1, 30, 3) // 遮罩开口开始
            // Opening2 = complexData(formData_.Opening2, 30, 3) // 遮罩开口结束

            // curveC_width = complexData(30, 30, 5) // 为了计算曲线的开始点位置不在 光源和起始开口 的连线上

            // curveC_x = complexData(30, 30, 3)   // 为了计算曲线的开始点位置不在 光源和起始开口 的连线上

            // var arrList = combinationFn2([SourceX, SourceY, MaskX, MaskR, Opening1, Opening2, curveC_width])
            var arrList = combinationFn([SourceY, MaskX])

            index = 0
            var time = setInterval(() => {
                var ele = arrList[index]
                formData_.SourceY = ele[0];
                formData_.MaskX = ele[1];

                formData_.index = index
                formData_.choice = true

                // console.log('', formData_)

                verification(formData_) // 展示所有图形
                index++;
                if (index > arrList.length - 1)
                    clearInterval(time), formData_.choice = false
            }, 20);

            console.log('', arrList.length)
        }

        // 限制光源在开口的垂线中间
        function Source(formData) {
            var focusStart = focusTip(formData.MaskX, formData.MaskY, formData.MaskR, formData.Opening1) // 遮罩开口开始点的坐标
            var focusEnd = focusTip(formData.MaskX, formData.MaskY, formData.MaskR, +formData.Opening1 + +formData
                .Opening2) // 遮罩开口结束点的坐标
            var mid = [(focusStart[0] + focusEnd[0]) / 2, (focusStart[1] + focusEnd[1]) / 2]
            x = mid[0] - (formData.SourceY - mid[1]) * (focusStart[1] - focusEnd[1]) / (focusStart[0] - focusEnd[0])
            formData.SourceX = x
        }

        // 限定规则
        function verification(formData) {
            // 条件：圆底部和底线(图中橙色先)相切，也就是它的圆心离下边框的距离不能大于半径r
            formData.MaskY = formData.CanvasWeight - formData.MaskR
            // 规则：限制光源在开口的垂线中间
            Source(formData)
            // 规则1：光源高度 > 画布宽度-圆半径，否则默认等于画布高度
            // if (formData.SourceY < formData.CanvasWeight - formData.MaskR) formData.SourceY = formData.CanvasWeight;
            // 规则1：光源高度 > 画布宽度-圆半径，否则退出
            if (formData.SourceY < formData.CanvasWeight - formData.MaskR) return;


            // var goldenLine = formData.CanvasWeight / 2; // 中间长度
            // var goldenLine_2 = formData.CanvasWeight * 0.618; // 黄金分割

            var goldenLine = formData.curveC_height; // 黄金分割
            var goldenLine_2 = formData.curveC_height + (formData.CanvasWeight * 0.618 - formData.CanvasWeight / 2); // 中间长度 

            var goldenLine_2_2 = formData.goldenLine_2_2 = goldenLine_2 / 2; // 黄金分割的一半
            var focusStart = focusTip(formData.MaskX, formData.MaskY, formData.MaskR, formData.Opening1) // 遮罩开口开始点的坐标
            var focusEnd = focusTip(formData.MaskX, formData.MaskY, formData.MaskR, +formData.Opening1 + +formData
                .Opening2) // 遮罩开口结束点的坐标
            var focusX_Start = focusX(formData.SourceX, formData.SourceY, focusStart[0], focusStart[
                1]) // 光源通过起始开口和x轴相切的点
            var focusX_End = focusX(formData.SourceX, formData.SourceY, focusEnd[0], focusEnd[1]) // 光源通过结束开口和x轴相切的点
            // var focusX_Start = focusX(formData.CanvasWeight * 0.19, formData.CanvasWeight, focusStart[0], focusStart[1])  // 光源通过起始开口和x轴相切的点
            // var focusX_End = focusX(formData.CanvasWeight * 0.19, formData.CanvasWeight, focusEnd[0], focusEnd[1])// 光源通过结束开口和x轴相切的点

            formData.focusStart = focusStart
            formData.focusEnd = focusEnd
            formData.focusX_Start = focusX_Start
            formData.focusX_End = focusX_End

            var curveStart = focus(formData.SourceX, formData.SourceY, focusStart[0], focusStart[1], null,
                goldenLine) // 计算曲线的开始点
            curveStart[0] += +formData.curveC_width || 25; // 为了计算曲线的开始点位置不在 光源和起始开口 的连线上
            var curveStart_X = focus(formData.SourceX, formData.SourceY, curveStart[0], curveStart[1], null,
                0) // 计算曲线的开始点和光源的连线通过x轴的坐标
            var curveEnd = focus(formData.SourceX, formData.SourceY, focusEnd[0], focusEnd[1], null,
                goldenLine_2) // 计算曲线的结束点
            curveEnd[0] -= +formData.curveC_width || 25;
            var curveEnd_X = focus(formData.SourceX, formData.SourceY, curveEnd[0], curveEnd[1], null,
                0) // 计算曲线的结束点和光源的连线通过x轴的坐标
            if (!curveStart || !curveStart_X || !curveEnd || !curveEnd_X) return; // 要判断一下，避免和x轴平行

            // curveC;//  曲线的控制点
            formData.curveC = [formData.curveC_x || 80, formData.curveC_y || 240]
            formData.curveStart = curveStart
            formData.curveStart_X = curveStart_X
            formData.curveEnd = curveEnd
            formData.curveEnd_X = curveEnd_X

            // 规则：曲线的开始点和光源的连线通过x轴的坐标，该坐标不能小于 光源通过起始开口和x轴相切的点 和不能大于 光源通过结束开口和x轴相切的点
            if (curveStart_X[0] <= focusX_Start[0] || curveStart_X[0] >= focusX_End[0]) return;
            // 规则：曲线的结束点和光源的连线通过x轴的坐标，该坐标不能小于 光源通过起始开口和x轴相切的点 和不能大于 光源通过结束开口和x轴相切的点、
            if (curveEnd_X[0] <= focusX_Start[0] || curveEnd_X[0] >= focusX_End[0]) return;


            PointTotal = CubicBezier(curveStart[0], curveStart[1], formData.curveC[0], formData.curveC[1], curveEnd[0],
                curveEnd[1])
            for (let index = 1; index < PointTotal.length - 1; index++) {
                const element = PointTotal[index];
                var tt = focus(formData.SourceX, formData.SourceY, element[0], element[1], null,
                    0) // 计算曲线上的所有点和光源的连线通过x轴的坐标
                // 规则：曲线上的点和光源的连线在x轴上的坐标不能小于 曲线的结束点和光源的连线通过x轴的坐标 和不能大于 曲线的结束点和光源的连线通过x轴的坐标
                // if (tt[0] <= curveStart_X[0] || tt[0] >= curveEnd_X[0]) return;
            }

            // 上面反向遮罩曲线
            var direction_Start = segmentsIntr({
                x: formData.SourceX,
                y: formData.SourceY
            }, {
                x: formData.curveEnd[0],
                y: formData.curveEnd[1]
            }, {
                x: formData.CanvasWeight - formData.SourceX,
                y: formData.SourceY
            }, {
                x: formData.CanvasWeight - formData.focusEnd[0],
                y: formData.focusEnd[1]
            }) // 获取两直线交点：光源、结束曲线和反光源、起始开口的连线
            var direction_End = segmentsIntr({
                x: formData.SourceX,
                y: formData.SourceY
            }, {
                x: formData.focusStart[0],
                y: formData.focusStart[1]
            }, {
                x: formData.CanvasWeight - formData.SourceX,
                y: formData.SourceY
            }, {
                x: formData.CanvasWeight - formData.curveStart[0],
                y: formData.curveStart[1]
            }) // 获取两直线交点：光源、起始开口的连线和反光源、曲线起点
            // var direction_End = focus(formData.SourceX, formData.SourceY, curveStart[0], curveStart[1], null, direction_Start.y - goldenLine_2) // 计算方向曲线的结束点的坐标
            // // var direction_End = focus(formData.SourceX, formData.SourceY, curveStart[0], curveStart[1], null, direction_Start.y) // 计算方向曲线的结束点的坐标
            // formData.direction_End = direction_End
            formData.direction_Start = direction_Start
            formData.direction_End = direction_End

            setCanvas(formData)
        }

        // setCanvas(formData)
        function setCanvas(formData) {
            if (Object.keys(formData).length == 0) return;

            // var canvas = document.getElementById(`canvas-${count}`);
            var canvas;
            if (formData.choice) {
                $("#myCanvasList").append(
                    `<canvas id="myCanvasList-${index}" width="${formData.CanvasWeight}" height="${formData.CanvasWeight}"></canvas>`
                )
                canvas = document.getElementById(`myCanvasList-${formData.index}`);
            } else canvas = myCanvas = document.getElementById(`myCanvas`);
            if (canvas.getContext) {
                var context = canvas.getContext('2d');
                clearCanvas(context)

                // 上面反向遮罩曲线  start


                // context.beginPath();
                // context.moveTo(formData.direction_start_x, formData.direction_start_y); // 画遮罩开口开始，靠里边
                // context.quadraticCurveTo(formData.direction_C_x, formData.direction_C_y, formData.direction_end_x, formData.direction_end_y); // 控制点和曲线接收点
                // context.lineTo(formData.CanvasWeight - formData.curveEnd_X[0], formData.curveEnd_X[1]); // 画光源与曲线结束点连线的x轴的连线(另一半的)
                // context.lineTo(formData.focusX_Start[0], formData.focusX_Start[1]); // 画光源与遮罩开口开始的连线
                // context.closePath();
                // context.fillStyle = '#cc4057';
                // // context.fillStyle = '#' + Math.floor(Math.random() * 16777215).toString(16);
                // context.fill();


                // context.beginPath();
                // // context.moveTo(formData.curveEnd[0], formData.curveEnd[1]); // 画遮罩开口开始，靠里边
                // context.moveTo(formData.CanvasWeight - formData.direction_start_x, formData.direction_start_y); // 画遮罩开口开始，靠里边
                // context.quadraticCurveTo(formData.CanvasWeight - formData.direction_C_x, formData.direction_C_y, formData.CanvasWeight - formData.direction_end_x, formData.direction_end_y); // 控制点和曲线接收点
                // context.lineTo(formData.curveEnd_X[0], formData.curveEnd_X[1]); // 画光源与曲线结束点连线的x轴的连线(另一半的)
                // context.lineTo(formData.CanvasWeight - formData.focusX_Start[0], formData.focusX_Start[1]); // 画光源与遮罩开口开始的连线
                // context.closePath();
                // context.fillStyle = '#cc4057';
                // // context.fillStyle = '#' + Math.floor(Math.random() * 16777215).toString(16);
                // context.fill();


                context.beginPath();
                context.moveTo(formData.curveStart[0], formData.curveStart[1]); // 曲线起点
                context.quadraticCurveTo(formData.direction_C_x, formData.direction_C_y, formData.curveEnd[0], formData
                    .curveEnd[1]); // 控制点和曲线接收点
                context.lineTo(formData.direction_Start.x, formData.direction_Start.y); // 两条直线的焦点
                context.lineTo(formData.CanvasWeight - formData.curveEnd_X[0], formData.curveEnd_X[
                    1]); // 画光源与曲线结束点连线的x轴的连线(另一半的)
                context.lineTo(formData.focusX_Start[0], formData.focusX_Start[1]); // 画光源与遮罩开口开始的连线
                context.closePath();
                context.fillStyle = '#cc4057';
                // context.fillStyle = '#' + Math.floor(Math.random() * 16777215).toString(16);
                context.fill();

                context.beginPath();
                context.moveTo(formData.CanvasWeight - formData.curveStart[0], formData.curveStart[1]); // 曲线起点
                context.quadraticCurveTo(formData.CanvasWeight - formData.direction_C_x, formData.direction_C_y, formData.CanvasWeight - formData.curveEnd[0], formData
                    .curveEnd[1]); // 控制点和曲线接收点
                context.lineTo(formData.CanvasWeight - formData.direction_Start.x, formData.direction_Start.y); // 两条直线的焦点
                context.lineTo(formData.curveEnd_X[0], formData.curveEnd_X[
                    1]); // 画光源与曲线结束点连线的x轴的连线(另一半的)
                context.lineTo(formData.CanvasWeight - formData.focusX_Start[0], formData.focusX_Start[1]); // 画光源与遮罩开口开始的连线
                context.closePath();
                context.fillStyle = '#cc4057';
                // context.fillStyle = '#' + Math.floor(Math.random() * 16777215).toString(16);
                context.fill();

                
                // arc(formData.direction_Start.x, formData.direction_Start.y, 5, context)
                arc(formData.direction_End.x, formData.direction_End.y, 15, context)
                // 上面反向遮罩曲线  end


                // 相反的 -start
                context.beginPath();
                context.moveTo(formData.CanvasWeight - formData.focusStart[0], formData.focusStart[1]); // 画遮罩开口开始

                context.lineTo(formData.CanvasWeight - formData.focusX_Start[0], formData.focusX_Start[
                    1]); // 画光源与遮罩开口开始的连线

                context.lineTo(formData.CanvasWeight - formData.curveStart_X[0], formData.curveStart_X[
                    1]); // 画光源与曲线起点连线的x轴的连线
                context.lineTo(formData.CanvasWeight - formData.curveStart[0], formData.curveStart[1]); // 曲线起点

                context.quadraticCurveTo(formData.CanvasWeight - formData.curveC[0], formData.curveC[1], formData
                    .CanvasWeight - formData.curveEnd[0], formData.curveEnd[1]);

                context.lineTo(formData.CanvasWeight - formData.curveEnd_X[0], formData.curveEnd_X[
                    1]); // 画光源与曲线结束点连线的x轴的连线

                context.lineTo(formData.CanvasWeight - formData.focusX_End[0], formData.focusX_End[1]); // 画光源与遮罩开口结束的连线

                context.lineTo(formData.CanvasWeight - formData.focusEnd[0], formData.focusEnd[1]); // 画遮罩开口结束
                context.closePath();
                // context.fillStyle = '#' + Math.floor(Math.random() * 16777215).toString(16);
                context.fillStyle = '#cc4057';
                context.fill();

                // 遮挡部分圆的路径 
                arc(formData.CanvasWeight - formData.MaskX, formData.MaskY, formData.MaskR, context, '#cc4057')
                // 相反的 -end


                context.beginPath();
                context.moveTo(formData.focusStart[0], formData.focusStart[1]); // 画遮罩开口开始

                context.lineTo(formData.focusX_Start[0], formData.focusX_Start[1]); // 画光源与遮罩开口开始的连线

                context.lineTo(formData.curveStart_X[0], formData.curveStart_X[1]); // 画光源与曲线起点连线的x轴的连线
                context.lineTo(formData.curveStart[0], formData.curveStart[1]); // 曲线起点

                context.quadraticCurveTo(formData.curveC[0], formData.curveC[1], formData.curveEnd[0], formData
                    .curveEnd[1]);

                context.lineTo(formData.curveEnd_X[0], formData.curveEnd_X[1]); // 画光源与曲线结束点连线的x轴的连线

                context.lineTo(formData.focusX_End[0], formData.focusX_End[1]); // 画光源与遮罩开口结束的连线

                context.lineTo(formData.focusEnd[0], formData.focusEnd[1]); // 画遮罩开口结束
                context.closePath();
                // context.fillStyle = '#' + Math.floor(Math.random() * 16777215).toString(16);
                context.fillStyle = '#cc4057';
                context.fill();

                // 遮挡部分圆的路径
                arc(formData.MaskX, formData.MaskY, formData.MaskR, context, '#cc4057')

                // 开口的起始点
                // arc(formData.focusStart[0], formData.focusStart[1], 5, context, '#ffffff')

                // 开口的结束点
                // arc(formData.focusEnd[0], formData.focusEnd[1], 5, context, '#ffffff')

                // 光源
                // arc(formData.SourceX, formData.SourceY, 5, context)

                // 曲线的
                // arc(formData.curveStart[0], formData.curveStart[1], 5, context, '#ffffff')
                // arc(formData.curveEnd[0], formData.curveEnd[1], 5, context, '#ffffff')
                // arc(formData.curveStart_X[0], formData.curveStart_X[1], 5, context, '#ffffff')
                // arc(formData.curveEnd_X[0], formData.curveEnd_X[1], 5, context, '#ffffff')
            }
        }

        /* 
            获取贝塞尔曲线的坐标
            https://www.jianshu.com/p/8f82db9556d2
            @x0, y0     标识贝塞尔曲线的起始坐标
            @x1, y1     标识贝塞尔曲线的控制点坐标
            @x2, y2     标识贝塞尔曲线的终点坐标
        */
        function CubicBezier(x0, y0, x1, y1, x2, y2) {
            let t1 = 0,
                max = 1,
                PointTotal = []

            function PointOnCubicBezier1(t) {
                var tPos = [0, 0]
                tPos[0] = Math.pow((1 - t), 2) * x0 + 2 * t * (1 - t) * x1 + Math.pow(t, 2) * x2;
                tPos[1] = Math.pow((1 - t), 2) * y0 + 2 * t * (1 - t) * y1 + Math.pow(t, 2) * y2;
                return tPos;
            }
            while (t1 < max) {
                point = PointOnCubicBezier1(t1 / max)
                t1 += 0.1;
                PointTotal.push(point)
            }
            return PointTotal
        }

        // 画圆
        function arc(x, y, r, context, color) {
            context.beginPath();
            context.arc(x, y, r, 0, 2 * Math.PI);
            if (color) context.fillStyle = color;
            else context.fillStyle = '#' + Math.floor(Math.random() * 16777215).toString(16);
            // context.fillStyle = '#' + Math.floor(Math.random() * 16777215).toString(16);
            // context.fillStyle = '#ffffff';
            context.fill();
            context.closePath();
        }

        // 清空画布
        function clearCanvas(context) {
            context.clearRect(0, 0, formData.CanvasWeight, formData.CanvasWeight);
        }


        /* 
            封装方法：统计一个区间之间按照指定宽度的一连串递增的数据
            @arrData {Array} 一个数组，只能有两个值，表示从一个值到另一个值。
            @Incremental {Number} 宽度，区间会按照该宽度进行地递增
        */
        function IncrementalFn(arrData, Incremental) {
            if (!Incremental || Incremental == 0) return;
            Incremental = Math.abs(Incremental)
            if (arrData[0] > arrData[1]) [arrData[0], arrData[1]] = [arrData[1], arrData[0]];
            var aArr = [];
            for (let index = Number(arrData[0]); index <= Number(arrData[1]);) {
                aArr.push(index);
                index = index + Incremental;
            }
            return aArr;
        }

        /* 
            一个数值，按照给定长度度取出区间，最后统计区间之间按照指定宽度的一连串递增的数据
            @target {Number} 一个数值
            @Incremental {Number} 长度，根据长度截取数值的上下区间
            @Incremental2 {Number} 宽度，区间会按照该宽度进行地递增
        */
        function complexData(target, Incremental, Incremental2 = 1) {
            return IncrementalFn([Number(target) - Incremental, Number(target) + Incremental], Incremental2)
        }


        /* 
            封装方法：设置有n个数组，每个数组中都有数据，现有需求：按照顺序从每一数组中取出一个元素，并按照取出元素的顺序添加到一个新的数组中，求有多少种组合
            ==注意==：必须要每个数组中都有数据，否则最终的返回结果是空数组。
            @doubleArrData {Array} 这是一个二维数组
        */
        function combinationFn(doubleArrData) {
            var total = [];
            (function d(arr, index_ = 0, tep = []) {
                for (let index = index_; index < arr.length; index++) {
                    const element = arr[index];

                    if (index != 0 && tep.length !== index) continue;

                    for (let index2 = 0; index2 < element.length; index2++) {
                        const element2 = element[index2];

                        tepClone = JSON.parse(JSON.stringify(tep));
                        tepClone.push(element2);
                        if (index < arr.length - 1) {
                            d(arr, index + 1, tepClone);
                        } else {
                            total.push(tepClone);
                        }
                    }
                }
            })(doubleArrData)
            return total;
        }
        combinationFn2 = (array) => {
            let resultArry = [];
            array.forEach((arrItem) => {
                if (resultArry.length === 0) {
                    resultArry = arrItem
                } else {
                    const emptyArray = [];
                    resultArry.forEach((item) => {
                        arrItem.forEach((value) => {
                            emptyArray.push([item, value])
                        })
                    })
                    resultArry = emptyArray
                }
            });
            return resultArry;
        }

        /* 
            根据已知每条直线上的任意两点，求两条直线的交点
            @a, b 为一条直线上的两个点的坐标
            @c, d 为另一条直线上的两个点的坐标
        */
        function segmentsIntr(a, b, c, d) {

            /** 1 解线性方程组, 求线段交点. **/
            // 如果分母为0 则平行或共线, 不相交  
            var denominator = (b.y - a.y) * (d.x - c.x) - (a.x - b.x) * (c.y - d.y);
            if (denominator == 0) {
                return false;
            }

            // 线段所在直线的交点坐标 (x , y)      
            var x = ((b.x - a.x) * (d.x - c.x) * (c.y - a.y) +
                (b.y - a.y) * (d.x - c.x) * a.x -
                (d.y - c.y) * (b.x - a.x) * c.x) / denominator;
            var y = -((b.y - a.y) * (d.y - c.y) * (c.x - a.x) +
                (b.x - a.x) * (d.y - c.y) * a.y -
                (d.x - c.x) * (b.y - a.y) * c.y) / denominator;

            /** 2 判断交点是否在两条线段上 **/
            // if (
            //     // 交点在线段1上  
            //     (x - a.x) * (x - b.x) <= 0 && (y - a.y) * (y - b.y) <= 0
            //     // 且交点也在线段2上  
            //     && (x - c.x) * (x - d.x) <= 0 && (y - c.y) * (y - d.y) <= 0
            // ) {
            //     // 返回交点p  
            //     return {
            //         x: x,
            //         y: y
            //     }
            // }
            return {
                x: x,
                y: y
            }
            //否则不相交  
            return false

        }

        /* 
            （两点式）：根据已知两点和一个x轴或y轴的值，求另一个y轴或x轴的值
            @x1, y1 第一个点
            @x2, y2 第二个点
            @x, y  连个中一个是确定值，求另一个的值
        */
        function focus(x1, y1, x2, y2, x, y) {
            for (let index = 0; index < arguments.length; index++) {
                arguments[index] = Number(arguments[index])
            }
            if (x1 == x2) {
                return null;
            }
            if (y1 == y2) {
                return null;
            }

            if (!x) {
                x = (y - y1) * (x2 - x1) / (y2 - y1) + x1
            }
            if (!y) {
                y = (x - x1) * (y2 - y1) / (x2 - x1) + y1;
            }
            return [x, y]
        }

        /* 
            （两点式）：根据已知两点，确定直线和x轴相交的点
            @x1, y1 第一个点
            @x2, y2 第二个点
        */
        function focusX(x1, y1, x2, y2, x, y) {
            for (let index = 0; index < arguments.length; index++) {
                arguments[index] = Number(arguments[index])
            }
            if (y1 == y2) {
                console.error("y轴平行了")
                return [0, 0];
            }
            // x = (y - y1) * (x2 - x1) / (y2 - y1) + x1
            x = (0 - y1) * (x2 - x1) / (y2 - y1) + x1
            return [x, 0]
        }

        /* 
            通过圆形角(角度)、圆心和半径，来确定弧度的交点
            @x 圆心
            @y 圆心
            @angle 斜边所对的角度
            @r 斜边长度
            @CanvasWeight 画布宽度
        */
        function focusTip(x, y, r, angle) {
            for (let index = 0; index < arguments.length; index++) {
                arguments[index] = Number(arguments[index])
            }
            angle = Number(angle)
            // if (angle > 180) angle = 170
            if (angle == 0) return [x - r, y]
            if (angle == 180) return [x + r, y]
            if (angle == 90) return [x, y - r]
            if (angle == 270) return [x, y + r]

            var h = oppositeSide(angle, r) // 对边长度
            var w = adjacentSide(angle, r) // 邻边长度
            var focus;

            // if (angle < 360) {
            //     focus = [x - w, y + h];
            // } if (angle < 270) {
            //     focus = [x + w, y + h];
            // } if (angle < 180) {
            //     focus = [x + w, y - h];
            // } if (angle < 90) {
            //     focus = [x - w, y - h];
            // }

            focus = [x - w, y - h];

            return focus;
        }


        /* 
            通过角度和斜边长度，确认对边长度
            @angle 斜边所对的角度
            @kw 斜边长度
        */
        function oppositeSide(angle, kw) {
            for (let index = 0; index < arguments.length; index++) {
                arguments[index] = Number(arguments[index])
            }
            // 度和弧度（角度和弧度）   http://c.biancheng.net/ref/62.html
            // return Math.abs(Math.sin(angle * (Math.PI / 180)) * kw)
            return Math.sin(angle * (Math.PI / 180)) * kw
        }
        /* 
            通过角度和斜边长度，确认邻边长度
            @angle 斜边所对的角度
            @kw 斜边长度
        */
        function adjacentSide(angle, kw) {
            for (let index = 0; index < arguments.length; index++) {
                arguments[index] = Number(arguments[index])
            }
            // 度和弧度（角度和弧度）   http://c.biancheng.net/ref/62.html
            // return Math.abs(Math.cos(angle * (Math.PI / 180)) * kw)
            return Math.cos(angle * (Math.PI / 180)) * kw
        }
    </script>
</body>

</html>